<?php
/**
*
* CHANGELOG
*
*
*
*
* Uses Singleton
*/

class Yz_HTTP_Request
    extends Yz_Class
    implements Yz_HTTP_Request_Interface
{

    protected static $instance;

    // "environment"
    protected $acceptLanguage; // Accept-Language
    protected $date; // READONLY
    protected $host; // READONLY
    protected $hostname; // READONLY
    protected $ifModifiedSince; // READONLY
    protected $isPOST; // READONLY
    protected $lang; // from HTTP Accept-Language ?
    protected $method; // READONLY GET|POST
    protected $path; // the full 'path' of the request
    protected $query; // QUERY_STRING
    protected $remoteAddr; // READONLY
    protected $scriptName; // READONLY

    protected $node;
    protected $session;
    protected $user;

    protected $arguments;

    /**
     * Yz_Web_Request::__construct()
     *
     * @return
     */
    protected function __construct()
    {
        debug();

        if ( ! isset( $_SERVER['REQUEST_URI'] ) ) {
            throw new Yz_Exception( "REQUEST_URI" );
        }

        $path = strtok( $_SERVER['REQUEST_URI'], '?' );

        // XXX  if rewrite is not active, path may be derived from query string:
        // XXX  /index.php?path

        debug("path: '$path'");

        $this->path = $path;

    } // __construct

    /**
     * Yz_Web_Request::getInstance()
     *
     * @return Yz_Web_Request
     */
    public static function getInstance()
    {
        debug();
        if( null === self::$instance ) {
            self::$instance = new self();
        }
        return self::$instance;
    } // getInstance

    /**
     *
     * @param string $key
     * @return string
     */
    protected function adjustKey( $key )
    {
        $key = Yz_String::testNotSpace( $key, 'request variable name' );
        return $key;
    } // adjustKey

    /**
     * fetch an argument from the GET parameters
     *
     * @param string $key
     *  @param mixed default: the type of returned argument must match the type of default
     * @return mixed
     */
    public function fetchArgument( $key, $default=null )
    {
        debug();
        return $this->fetchArgumentValue( $key, $default, $_GET );
    } // fetchArgument



    /**
     * fetch an argument from the POST parameters
     *
     * @param string $key
     *  @param mixed default: the type of returned argument must match the type of default
     * @return mixed
     */
    public function fetchPOSTArgument( $key, $default=null )
    {
        return $this->fetchArgumentValue( $key, $default, $_POST );
    } // fetchPOSTArgument


    /**
     *  @param mixed default: if not null, the type of returned argument will match the type of default
     */
    protected function fetchArgumentValue( $key, $default, $args )
    {
        // throws exception
        $key = $this->adjustKey( $key );

        if (array_key_exists( $key, $args )) {
            $arg = $args[$key];
            if (null === $default) {
                return $arg;
            }
            if (is_bool($default)) {
                return is_string($arg) ? Yz_String::isTrue($arg) : $default;
            }
            if (is_string($default)) {
                return is_string($arg) ? $arg : $default;
            }
            if (is_array($default)) {
                return is_array($arg) ? $arg : $default;
            }
        }
        return $default;
    }

    /**
     * fetch a custom Header
     *
     * @param string $header
     * @return string|null
     */
    //TODO: verify header contents
    public function fetchHeader( $header )
    {
        debug();

        // throws exception
        $header = Yz_String::testNotEmpty( $header, 'header name' );

        $header = 'HTTP_' . strtoupper( $header );
        $r = isset($_SERVER[$header]) ? $_SERVER[$header] : null;
        return $r;
    } // fetchHeader()


    /**
     * Yz_Web_Request::toString()
     *
     * @return string
     * @todo something more meaningful
     */
    public function __toString()
    {
        debug();

        return $this->path;
    } // toString


    /**
     * get the Host header
     *
     * @return string
     */
    public function getHost()
    {
        $host =& $this->host;
        if ( null === $host ) {
            $host = "";
            if ( isset( $_SERVER['HTTP_HOST'] ) ) {

                // may be an international host name, too
                $host = strtolower($_SERVER['HTTP_HOST']);

                debug("Host: {$host}");
                // contains invalid characters
                if( ! preg_match( '~[a-z0-9][a-z0-9\.-]*(?:\:\d+)~', $host ) ) {
                    debug("Host name invalid: {$host}");
                    $host = "";
                }
            }
        }
        return $host;
    }


    /**
     * get the Hostname of the running HTTP server
     *
     * @return string
     */
    public function getHostname()
    {
        $host =& $this->hostname;
        if ( null === $host ) {
            $host = $this->getHost();

            if ( "" === $host ) {
                // this must always be set
                $host = $_SERVER['SERVER_NAME'];
                if ( ! empty($_SERVER['SERVER_PORT']) && '80' !== (string)$_SERVER['SERVER_PORT'] ) {
                    $host .= ":" . $_SERVER['SERVER_PORT'];
                }
            }

        }
        return $host;
    } // getHostname

    /**
     * Yz_Web_Request::getIfModifiedSince()
     *
     * @return timestamp
     */
    public function getIfModifiedSince()
    {
        if( null === $this->ifModifiedSince ) {

            if( isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ) {
                $this->ifModifiedSince = (int)@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
            }
        }
        return $this->ifModifiedSince;
    } // getIfModifiedSince

    /**
     * Yz_Web_Request::getMethod()
     *
     * @return string
     */
    public function getMethod()
    {
        if( null === $this->method ) {
            $this->method = $_SERVER['REQUEST_METHOD'];
        }
        return $this->method;
    } // getMethod


    /**
     * getScriptName()
     *
     * @return string
     */
    public function getScriptName()
    {
        debug();
        if( null === $this->scriptName ) {
            $this->scriptName = $_SERVER['SCRIPT_NAME'];
        }
        return $this->scriptName;
    } // getScriptName

    /**
     * Yz_Web_Request::getAcceptLanguage()
     *
     * @return array?
     */
    public function getAcceptLanguage()
    {
        if( null === $this->acceptLanguage ) {
            $this->acceptLanguage = isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])
                ? $_SERVER['HTTP_ACCEPT_LANGUAGE']
                : null;
            /* TODO handle: obtain correct values */
        }
        return $this->acceptLanguage;
    } // getAcceptLanguage

    /**
     * Yz_Web_Request::getQuery()
     *
     * @return string
     */
    public function getQuery()
    {
        if( null === $this->query ) {
            $this->query = $_SERVER['QUERY_STRING'];
        }
        return $this->query;
    } // getMethod

    /**
     * Yz_Web_Request::getPath()
     *
     * @return string
     */
    public function getPath()
    {
        return $this->path;
    } // getPath


    /**
     * Yz_Web_Request::getRemoteAddr()
     *
     * @return string IP
     */
    public function getRemoteAddr()
    {
        if( null === $this->remoteAddr ) {
            $this->remoteAddr = $_SERVER['REMOTE_ADDR'];
        }
        return $this->remoteAddr;
    } // getRemoteAddr


    /**
     * Yz_Web_Request::isPOST()
     *
     * @return boolean
     */
    public function isPOST()
    {
        if( null === $this->isPOST ) {
            $this->isPOST = ( 'POST' === $this->getMethod() );
        }
        return $this->isPOST;
    } // isPOST


    /**
     * Yz_Web_Request::isAJAX()
     *
     * @return boolean
     */
    public function isAJAX()
    {
        if( null === $this->isAJAX ) {
            $this->isAJAX = isset( $_SERVER['HTTP_X_REQUEST_TYPE'] );
        }
        return $this->isAJAX;
    } // isAJAX


    /**
     * Yz_HTTP_Request::getArguments()
     *
     * @return Yz_Hash arguments, pruned
     */
    public function getArguments()
    {
        debug();
        $args = $_GET;
        // TODO prune: remove empty values
        // rename arrays or remove them at all
        return new Yz_Hash($args);
    } // getArguments()

    /**
     * Yz_HTTP_Request::getPOSTArguments()
     *
     * @return Yz_Hash arguments, pruned
     */
    public function getPOSTArguments()
    {
        debug();
        $args = $_POST;
        // TODO prune: remove empty values
        // rename arrays or remove them at all
        return new Yz_Hash($args);
    } // getPOSTArguments()



} // class Yz_HTTP_Request

